package tc.jv.task4;

import java.io.Serializable;
import java.util.Locale;

import tc.jv.task9.IncompatibleVectorSizeException;

public class LinkedListVector extends AbstractVector implements Cloneable
{
	
	private static final long serialVersionUID = -7303473103739768142L;

	protected class Node implements Serializable
	{
		
		private static final long serialVersionUID = 6320334515848047625L;
		
		public double value;
		public Node prev,
					next;
		
		// CONSTRUCTORS
		
		public Node(double value)
		{
			this.value = value;
		}
		
	}

	protected Node head;
	protected int length;
	
	// CONSTRUCTORS
	
	public LinkedListVector()
	{
		head = null;
		length = 0;
	}
	
	public LinkedListVector(int size)
	{
		for (int i = 0; i < size; i++)
			add(0);
	}
	
	// ACCESSORS
	
	@Override
	public int size()
	{
		return length;
	}

	@Override
	public double get(int index)
	{
		Node node = findNode(index);
		return node.value;
	}
	
	@Override
	public double set(int index, double value)
	{
		Node node = findNode(index);		
		double oldValue = node.value;
		
		node.value = value;
		return oldValue;
	}
	
	// SUPERCLASS METHODS
	
	// TODO Clone
	@Override
	public Object clone()
	{
		LinkedListVector v = null;
		v = (LinkedListVector) super.clone();
		Node node = head;
		for (int i = 0; i < length; i++, node = node.next)
			v.add(node.value);
		
		return v;
	}

	@Override
	public boolean equals(Object obj)
	{
		if (obj == this)
			return true;
		if (obj instanceof LinkedListVector)
		{
			LinkedListVector v = (LinkedListVector) obj;
			if (this.length == v.length)
			{	
				Node node  = head,
					 vnode = v.head;
				boolean e = true;
	
				for (int i = 0; e == true && i < length; i++, node = node.next, vnode = vnode.next)
					e &= (node.value == vnode.value);
				
				return e;
			}				
			else
				return false;
		}
		else if (obj instanceof Vector)
		{
			Vector v = (Vector) obj;
			if (this.length == v.size())
			{	
				Node node = head;
				boolean e = true;
	
				for (int i = 0; e == true && i < length; i++, node = node.next)
					e &= (node.value == v.get(i));
				
				return e;
			}				
			else
				return false;
		}
		else
			return false;
	}

	@Override
	public int hashCode()
	{
		Node node = head;
		long lhc = 0;
		
		for (int i = 0; i < length; i++, node = node.next)
			lhc ^= (long) node.value;
		
		int hc = (int) (lhc >> 32) ^ (int) lhc;
		
		return hc;		
	}

	// METHODS

	protected String formAString(String format)
	{
		String s = "";
		
		Node node = head;
		for (int i = 0; i < length; i++, node = node.next)
			s = s + String.format(Locale.US, format, node.value);
		return s;
	}
	
	protected void addFirstNode(double value)
	{
		Node node = new Node(value);
		
		head = node;
		head.prev = head;
		head.next = head;
				
		length++;
	}
	
	@Override
	public void addFirst(double value)
	{
		if (head != null)
		{
			add(0, value);
			head = head.prev;
		}
		else
			addFirstNode(value);
	}

	@Override
	public void addLast(double value)
	{
		if (head != null)
			add(value);
		else
			addFirstNode(value);
	}

	public void add(double value)
	{		
		if (head != null)
		{
			Node node = new Node(value);
			
			Node p = head.prev;
			
			head.prev = node;
			node.next = head;
			
			p.next = node;
			node.prev = p;
			
			length++;
		}
		else
			addFirstNode(value);
	}
	
	@Override
	public boolean add(int index, double value)
	{		
		Node node = findNode(index);

		Node newNode = new Node(value);
		Node prev = node.prev;
			
		prev.next = newNode;
		newNode.prev = prev;
			
		node.prev = newNode;
		newNode.next = node;
			
		length++;
		
		return true;
	}

	public double remove(int index)
	{
		Node node = findNode(index);
		double oldValue = node.value;

		Node prev = node.prev;
		Node next = node.next;
			
		if (node == head)
		{
			if (head.next == head)
				head = null;
			else
				head = next;
		}
		else
		{
			prev.next = next;
			next.prev = prev;
		}
			
		length--;

		return oldValue;
	}
	
	@Override
	public boolean fill(double[] array) throws IncompatibleVectorSizeException
	{
		return fill(array, 0);
	}

	@Override
	public boolean fill(double[] array, int position) throws IncompatibleVectorSizeException
	{
		vectorArgumentCheck(array, position);

		Node node = findNode(position);
			
		for (int i = 0; i < array.length; i++, node = node.next)
			node.value = array[i];
		
		return true;
	}

	@Override
	public boolean fill(Vector v) throws IncompatibleVectorSizeException
	{
		return fill(v, 0);
	}

	@Override
	public boolean fill(Vector v, int position) throws IncompatibleVectorSizeException
	{
		vectorArgumentCheck(v, position);
		
		if (v instanceof LinkedListVector)
		{
			LinkedListVector llv = (LinkedListVector) v;
			Node node  = findNode(position),
				 vnode = llv.head;
			for (int i = 0; i < llv.length; i++, node = node.next, vnode = vnode.next)
				node.value = vnode.value;
		}
		else
		{
			Node node = findNode(position);
			for (int i = 0; i < v.size(); i++, node = node.next)
				node.value = v.get(i);
		}
		
		return true;
	}

	@Override
	public boolean sum(Vector v) throws IncompatibleVectorSizeException
	{
		return sum(v, 0);
	}

	@Override
	public boolean sum(Vector v, int position) throws IncompatibleVectorSizeException
	{
		vectorArgumentCheck(v, position);
		
		LinkedListVector llv = (LinkedListVector) v;
		Node node = findNode(position),
			 vnode = llv.head;			
		for (int i = 0; i < v.size(); i++, node = node.next, vnode = vnode.next)
			node.value += vnode.value;
		
		return true;
	}

	@Override
	public boolean multiply(double multiplicator)
	{
		multiplicatorCheck(multiplicator);
		
		Node node = head;
		for (int i = 0; i < length; i++, node = node.next)
			node.value *= multiplicator;
		
		return true;
	}

	protected Node findNode(int index)
	{
		indexCheck(index);
		
		Node node = head;
		for (int i = 0; i < index; i++)
			node = node.next;
		return node;			
	}

}

